stubdom: use PVFB so as to e.g. permit SDL display
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 28 Feb 2008 10:21:21 +0000 (10:21 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 28 Feb 2008 10:21:21 +0000 (10:21 +0000)
This adds support in ioemu for PVFB frontend as stubdomain display.
This permits for instance to use SDL in dom0 to perform the eventual
display.

Signed-off-by: Samuel Thibault <samuel.thibault@eu.citrix.com>
extras/mini-os/fbfront.c
extras/mini-os/include/fbfront.h
stubdom/README
tools/ioemu/hw/xenfb.c
tools/ioemu/vl.c
tools/ioemu/vl.h
tools/ioemu/xenstore.c

index bbe49b92a509f2590c79107aca71d2fd936a9f0c..4f5041ab6972c80d6d9dfd1c26262a7c59174d54 100644 (file)
@@ -31,13 +31,6 @@ struct kbdfront_dev {
     char *nodename;
     char *backend;
 
-    char *data;
-    int width;
-    int height;
-    int depth;
-    int line_length;
-    int mem_length;
-
 #ifdef HAVE_LIBC
     int fd;
 #endif
@@ -316,7 +309,10 @@ struct fbfront_dev *init_fbfront(char *nodename, void *data, int width, int heig
     for (i = 0; mapped < mem_length && i < max_pd; i++) {
         unsigned long *pd = (unsigned long *) alloc_page();
         for (j = 0; mapped < mem_length && j < PAGE_SIZE / sizeof(unsigned long); j++) {
-            pd[j] = virt_to_mfn((unsigned long) data + mapped);
+            /* Trigger CoW */
+            * ((char *)data + mapped) = 0;
+            barrier();
+            pd[j] = virtual_to_mfn((unsigned long) data + mapped);
             mapped += PAGE_SIZE;
         }
         for ( ; j < PAGE_SIZE / sizeof(unsigned long); j++)
index 502f0330a205c2bab774841ae5cb41a55489db46..fa98942e76372487908cb5345b8d8ae4ea57de0c 100644 (file)
@@ -14,6 +14,9 @@
 #ifndef KEY_Q
 #define KEY_Q 16
 #endif
+#ifndef KEY_MAX
+#define KEY_MAX 0x1ff
+#endif
 
 
 struct kbdfront_dev;
index 4b9bef6b19744fb30685af63a8b37f821910b61a..d4e1664b4f1a6ef7f39bf5d520103bedc8e609ad 100644 (file)
@@ -6,39 +6,78 @@ Then make install to install the result.
 
 Also, run make and make install in $XEN_ROOT/tools/fs-back
 
-To run
-======
-
-mkdir -p /exports/usr/share/qemu
-ln -s /usr/share/qemu/keymaps /exports/usr/share/qemu
-/usr/sbin/fs-backend &
-
+General Configuration
+=====================
 
 In your HVM config "hvmconfig",
 
-- use VNC, set vnclisten to "172.30.206.1" for instance.  Do not use a host name
-as Mini-OS does not have a name resolver.  Do not use 127.0.0.1 since then you
-will not be able to connect to it.
-
-vnc = 1
-vnclisten = "172.30.206.1"
-
 - use /usr/lib/xen/bin/stubdom-dm as dm script
 
 device_model = '/usr/lib/xen/bin/stubdom-dm'
 
 - comment the disk statement:
+
 #disk = [  'file:/tmp/install.iso,hdc:cdrom,r', 'phy:/dev/sda6,hda,w', 'file:/tmp/test,hdb,r' ]
 
-Create /etc/xen/stubdom-hvmconfig (where "hvmconfig" is your HVM guest domain
-name) with
+
+Create /etc/xen/stubdom-hvmconfig (where "hvmconfig" is the name of your HVM
+guest) with
 
 kernel = "/usr/lib/xen/boot/stubdom.gz"
-vif = [ 'ip=172.30.206.1', 'ip=10.0.1.1,mac=aa:00:00:12:23:34']
+vif = [ '', 'ip=10.0.1.1,mac=aa:00:00:12:23:34']
 disk = [  'file:/tmp/install.iso,hdc:cdrom,r', 'phy:/dev/sda6,hda,w', 'file:/tmp/test,hdb,r' ]
 
 where
-- 172.30.206.1 is the IP for vnc,
+- the first vif ('') is reserved for VNC (see below)
 - 'ip=10.0.1.1,mac= etc...' is the same net configuration as in the hvmconfig
 script,
 - and disk = is the same block configuration as in the hvmconfig script.
+
+Display Configuration
+=====================
+
+There are three posibilities
+
+* Using SDL
+
+In hvmconfig, disable vnc:
+
+vnc = 0
+
+In stubdom-hvmconfig, set a vfb:
+
+vfb = [ 'type=sdl' ]
+
+* Using a VNC server in the stub domain
+
+In hvmconfig, set vnclisten to "172.30.206.1" for instance.  Do not use a host
+name as Mini-OS does not have a name resolver.  Do not use 127.0.0.1 since then
+you will not be able to connect to it.
+
+vnc = 1
+vnclisten = "172.30.206.1"
+
+In stubdom-hvmconfig, fill the reserved vif with the same IP, for instance:
+
+vif = [ 'ip=172.30.206.1', 'ip=10.0.1.1,mac=aa:00:00:12:23:34']
+
+* Using a VNC server in dom0
+
+In hvmconfig, disable vnc:
+
+vnc = 0
+
+In stubdom-hvmconfig, set a vfb:
+
+vfb = [ 'type=vnc' ]
+
+and any other parameter as wished.
+
+To run
+======
+
+mkdir -p /exports/usr/share/qemu
+ln -s /usr/share/qemu/keymaps /exports/usr/share/qemu
+/usr/sbin/fs-backend &
+
+xm create hvmconfig
index ea4ea14db2102a63da21437187d016fa3fb0f18e..e2f181126e0dc46b2c4776ac9a181ab712c9b903 100644 (file)
 
 #include "xenfb.h"
 
+#ifdef CONFIG_STUBDOM
+#include <semaphore.h>
+#include <sched.h>
+#include <fbfront.h>
+#endif
+
 #ifndef BTN_LEFT
 #define BTN_LEFT 0x110 /* from <linux/input.h> */
 #endif
@@ -1124,12 +1130,10 @@ static void xenfb_guest_copy(struct xenfb *xenfb, int x, int y, int w, int h)
     dpy_update(xenfb->ds, x, y, w, h);
 }
 
-/* QEMU display state changed, so refresh the framebuffer copy */
-/* XXX - can we optimize this, or the next func at all ? */ 
+/* Periodic update of display, no need for any in our case */
 static void xenfb_update(void *opaque)
 {
     struct xenfb *xenfb = opaque;
-    xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height);
 }
 
 /* QEMU display state changed, so refresh the framebuffer copy */
@@ -1169,6 +1173,206 @@ static int xenfb_register_console(struct xenfb *xenfb) {
         return 0;
 }
 
+#ifdef CONFIG_STUBDOM
+static struct semaphore kbd_sem = __SEMAPHORE_INITIALIZER(kbd_sem, 0);
+static struct kbdfront_dev *kbd_dev;
+static char *kbd_path, *fb_path;
+
+static unsigned char linux2scancode[KEY_MAX + 1];
+
+#define WIDTH 1024
+#define HEIGHT 768
+#define DEPTH 32
+#define LINESIZE (1280 * (DEPTH / 8))
+#define MEMSIZE (LINESIZE * HEIGHT)
+
+int xenfb_connect_vkbd(const char *path)
+{
+    kbd_path = strdup(path);
+    return 0;
+}
+
+int xenfb_connect_vfb(const char *path)
+{
+    fb_path = strdup(path);
+    return 0;
+}
+
+static void xenfb_pv_update(DisplayState *s, int x, int y, int w, int h)
+{
+    struct fbfront_dev *fb_dev = s->opaque;
+    fbfront_update(fb_dev, x, y, w, h);
+}
+
+static void xenfb_pv_resize(DisplayState *s, int w, int h)
+{
+    struct fbfront_dev *fb_dev = s->opaque;
+    fprintf(stderr,"resize to %dx%d required\n", w, h);
+    s->width = w;
+    s->height = h;
+    /* TODO: send resize event if supported */
+    memset(s->data, 0, MEMSIZE);
+    fbfront_update(fb_dev, 0, 0, WIDTH, HEIGHT);
+}
+
+static void xenfb_pv_colourdepth(DisplayState *s, int depth)
+{
+    /* TODO: send redepth event if supported */
+    fprintf(stderr,"redepth to %d required\n", depth);
+}
+
+static void xenfb_kbd_handler(void *opaque)
+{
+#define KBD_NUM_BATCH 64
+    union xenkbd_in_event buf[KBD_NUM_BATCH];
+    int n, i;
+    DisplayState *s = opaque;
+    static int buttons;
+    static int x, y, z;
+
+    n = kbdfront_receive(kbd_dev, buf, KBD_NUM_BATCH);
+    for (i = 0; i < n; i++) {
+        switch (buf[i].type) {
+
+            case XENKBD_TYPE_MOTION:
+                fprintf(stderr, "FB backend sent us relative mouse motion event!\n");
+                break;
+
+            case XENKBD_TYPE_POS:
+            {
+                int new_x = buf[i].pos.abs_x;
+                int new_y = buf[i].pos.abs_y;
+                int new_z = buf[i].pos.abs_z;
+                if (new_x >= s->width)
+                    new_x = s->width - 1;
+                if (new_y >= s->height)
+                    new_y = s->height - 1;
+                if (kbd_mouse_is_absolute()) {
+                    kbd_mouse_event(
+                            new_x * 0x7FFF / (s->width - 1),
+                            new_y * 0x7FFF / (s->height - 1),
+                            new_z,
+                            buttons);
+                } else {
+                    kbd_mouse_event(
+                            new_x - x,
+                            new_y - y,
+                            new_z - z,
+                            buttons);
+                }
+                x = new_x;
+                y = new_y;
+                z = new_z;
+                break;
+            }
+
+            case XENKBD_TYPE_KEY:
+            {
+                int keycode = buf[i].key.keycode;
+                int button = 0;
+
+                if (keycode == BTN_LEFT)
+                    button = MOUSE_EVENT_LBUTTON;
+                else if (keycode == BTN_RIGHT)
+                    button = MOUSE_EVENT_RBUTTON;
+                else if (keycode == BTN_MIDDLE)
+                    button = MOUSE_EVENT_MBUTTON;
+
+                if (button) {
+                    if (buf[i].key.pressed)
+                        buttons |=  button;
+                    else
+                        buttons &= ~button;
+                    if (kbd_mouse_is_absolute())
+                        kbd_mouse_event(
+                                x * 0x7FFF / s->width,
+                                y * 0x7FFF / s->height,
+                                z,
+                                buttons);
+                    else
+                        kbd_mouse_event(0, 0, 0, buttons);
+                } else {
+                    int scancode = linux2scancode[keycode];
+                    if (!scancode) {
+                        fprintf(stderr, "Can't convert keycode %x to scancode\n", keycode);
+                        break;
+                    }
+                    if (scancode & 0x80) {
+                        kbd_put_keycode(0xe0);
+                        scancode &= 0x7f;
+                    }
+                    if (!buf[i].key.pressed)
+                        scancode |= 0x80;
+                    kbd_put_keycode(scancode);
+                }
+                break;
+            }
+        }
+    }
+}
+
+static void xenfb_pv_refresh(DisplayState *ds)
+{
+    vga_hw_update();
+}
+
+static void kbdfront_thread(void *p)
+{
+    int scancode, keycode;
+    kbd_dev = init_kbdfront(p, 1);
+    if (!kbd_dev) {
+        fprintf(stderr,"can't open keyboard\n");
+        exit(1);
+    }
+    up(&kbd_sem);
+    for (scancode = 0; scancode < 128; scancode++) {
+        keycode = atkbd_set2_keycode[atkbd_unxlate_table[scancode]];
+        linux2scancode[keycode] = scancode;
+        keycode = atkbd_set2_keycode[atkbd_unxlate_table[scancode] | 0x80];
+        linux2scancode[keycode] = scancode | 0x80;
+    }
+}
+
+int xenfb_pv_display_init(DisplayState *ds)
+{
+    void *data;
+    struct fbfront_dev *fb_dev;
+    int kbd_fd;
+
+    if (!fb_path || !kbd_path)
+        return -1;
+
+    create_thread("kbdfront", kbdfront_thread, (void*) kbd_path);
+
+    data = qemu_memalign(PAGE_SIZE, VGA_RAM_SIZE);
+    fb_dev = init_fbfront(fb_path, data, WIDTH, HEIGHT, DEPTH, LINESIZE, MEMSIZE);
+    if (!fb_dev) {
+        fprintf(stderr,"can't open frame buffer\n");
+        exit(1);
+    }
+    free(fb_path);
+
+    down(&kbd_sem);
+    free(kbd_path);
+
+    kbd_fd = kbdfront_open(kbd_dev);
+    qemu_set_fd_handler(kbd_fd, xenfb_kbd_handler, NULL, ds);
+
+    ds->data = data;
+    ds->linesize = LINESIZE;
+    ds->depth = DEPTH;
+    ds->bgr = 0;
+    ds->width = WIDTH;
+    ds->height = HEIGHT;
+    ds->dpy_update = xenfb_pv_update;
+    ds->dpy_resize = xenfb_pv_resize;
+    ds->dpy_colourdepth = NULL; //xenfb_pv_colourdepth;
+    ds->dpy_refresh = xenfb_pv_refresh;
+    ds->opaque = fb_dev;
+    return 0;
+}
+#endif
+
 /*
  * Local variables:
  *  c-indent-level: 8
index f9a575043d126f15271ac56722f57a743b56ed5a..aaecb286a9a29fff1bdb677fa19950fa36f73312 100644 (file)
@@ -7831,6 +7831,10 @@ int main(int argc, char **argv)
     init_ioports();
 
     /* terminal init */
+#ifdef CONFIG_STUBDOM
+    if (xenfb_pv_display_init(ds) == 0) {
+    } else
+#endif
     if (nographic) {
         dumb_display_init(ds);
     } else if (vnc_display != NULL || vncunused != 0) {
index 0c5a9da97f1705f38e0c0f23b1d1e7b8902e939b..5c421cff9c7e2f209b7491bed6cef8769d793244 100644 (file)
@@ -1527,6 +1527,11 @@ int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle,
 int xenstore_vm_write(int domid, char *key, char *val);
 char *xenstore_vm_read(int domid, char *key, unsigned int *len);
 
+/* xenfb.c */
+int xenfb_pv_display_init(DisplayState *ds);
+int xenfb_connect_vkbd(const char *path);
+int xenfb_connect_vfb(const char *path);
+
 /* helper2.c */
 extern long time_offset;
 void timeoffset_get(void);
index 843a10e87501c59a083173c8557bdc1ebe8eef3f..e1c253221b227e4cd7777cdd872d88798c6ff463 100644 (file)
@@ -238,6 +238,37 @@ void xenstore_parse_domain_config(int domid)
         }
     }
 
+#ifdef CONFIG_STUBDOM
+    if (pasprintf(&buf, "%s/device/vkbd", path) == -1)
+        goto out;
+
+    free(e);
+    e = xs_directory(xsh, XBT_NULL, buf, &num);
+
+    if (e) {
+        for (i = 0; i < num; i++) {
+            if (pasprintf(&buf, "%s/device/vkbd/%s", path, e[i]) == -1)
+                continue;
+            xenfb_connect_vkbd(buf);
+        }
+    }
+
+    if (pasprintf(&buf, "%s/device/vfb", path) == -1)
+        goto out;
+
+    free(e);
+    e = xs_directory(xsh, XBT_NULL, buf, &num);
+
+    if (e) {
+        for (i = 0; i < num; i++) {
+            if (pasprintf(&buf, "%s/device/vfb/%s", path, e[i]) == -1)
+                continue;
+            xenfb_connect_vfb(buf);
+        }
+    }
+#endif
+
+
     /* Set a watch for log-dirty requests from the migration tools */
     if (pasprintf(&buf, "/local/domain/0/device-model/%u/logdirty/next-active",
                   domid) != -1) {